#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <vector>
#include <map>

class debug_{
public:
	void log(string a, string b){}
};

using boost::property_tree::ptree;

class NclCache{

   	debug dbg;
	// Create empty property tree object
    	ptree ncl_data;


	map<MediaType, string> node_name_map;
	map<string,NodeType> node_type_map;
	map<string,MediaType> ext_type_map;

	map<string, vector<string> > portMedia;

	map<string, string> mediaSrc;
	map<string, string> mediaDesc;
	map<string, MediaType> mediaType;

	map<string, string> descRegion;

	map<string, map<string, string> > region;
	map<string, map<string, string> > bind;
	map<string, map<string, string> > condition;
	map<string, map<string, string> > action;

	vector<string> linkId;
	map<string, string> connector;

	map<string, vector<string> > onbegin;
	map<string, vector<string> > onend;
	map<string, vector<string> > onpause;
	map<string, vector<string> > onresume;
	map<string, vector<string> > onabort;

	map <string, TRANSITION> str_transition;

public:
	NclCache(string str_id, string file)
	{

		dbg.log(__PRETTY_FUNCTION__, "###############[Caching Ncl]#################");
		dbg.log(__PRETTY_FUNCTION__, getVersion() + " - caching[" + file + "]...");
		mapsPopulate();

		try
		{
    			read_xml(file, ncl_data, 2);
	    		string ncl_id = ncl_data.get<string>("ncl.<xmlattr>.id");
	    		string ncl_model = ncl_data.get<string>("ncl.<xmlattr>.xmlns");
			dbg.log(__PRETTY_FUNCTION__, "NCL_ID=" + ncl_id + " - Schema=" + ncl_model);

			loadData(str_id, ncl_data);	
			linkConnectors();
		}
    		catch (exception &e)
		{
			//cout << "Error: " << e.what() << "\n";
			dbg.log(__PRETTY_FUNCTION__,"Error: " + boost::lexical_cast<string>(e.what()));
		}
			dbg.log(__PRETTY_FUNCTION__, "#########[Ncl cached sucessfully]##########");
			dbg.log(__PRETTY_FUNCTION__, "                                           ");
	}

	~NclCache()
	{
		dbg.log(__PRETTY_FUNCTION__, "Bye Bye!");
	}

	void mapsPopulate()
	{
		node_name_map[MEDIA_NCL]="body";
		node_name_map[MEDIA_CONTEXT]="context";
		node_name_map[MEDIA_SWITCH]="switch";

		node_type_map["ncl"]=NODE_NCL;
		node_type_map["body"]=NODE_BODY;
		node_type_map["context"]=NODE_CONTEXT;
		node_type_map["switch"]=NODE_SWITCH;
		node_type_map["port"]=PORT;
		node_type_map["media"]=MEDIA;
		node_type_map["link"]=LINK;
		node_type_map["bind"]=BIND;
		node_type_map["descriptor"]=DESCRIPTOR;
		node_type_map["region"]=REGION;
		node_type_map["causalConnector"]=CAUSALCONNECTOR;
		node_type_map["simpleCondition"]=SIMPLECONDITION;
		node_type_map["simpleAction"]=SIMPLEACTION;

		ext_type_map["mp4"]=VIDEO;
		ext_type_map["mpg"]=VIDEO;
		ext_type_map["mp3"]=SOUND;
		ext_type_map["txt"]=TXT;
		ext_type_map["jpeg"]=IMAGE;
		ext_type_map["png"]=IMAGE;
		ext_type_map["lua"]=LUA;
	
		str_transition["STARTS"]=STARTS;
		str_transition["STOPS"]=STOPS;
		str_transition["PAUSES"]=PAUSES;
		str_transition["RESUMES"]=RESUMES;
		str_transition["ABORTS"]=ABORTS;
	}

	void loadData(string node_id, ptree node_base)
	{
		string node_name;
		string id;
		BOOST_FOREACH(ptree::value_type &v, node_base)
		{
			id="";
			node_name=v.first;
			dbg.log(__PRETTY_FUNCTION__,"subnode=" + node_name);

			try
			{
				switch(node_type_map[node_name])
				{
					case NODE_NCL:
					case NODE_BODY:
						loadData(node_id, v.second.get_child(""));
					break;
					case NODE_CONTEXT:
	   					id=v.second.get<string>("<xmlattr>.id");
						mediaType[id]=MEDIA_CONTEXT;
						loadData(id, v.second.get_child(""));
					break;
					case NODE_SWITCH:
	   					id=v.second.get<string>("<xmlattr>.id");
						mediaType[id]=MEDIA_SWITCH;
						loadData(id, v.second.get_child(""));
					break;
					case PORT:
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "component=" + v.second.get<string>("<xmlattr>.component"));
	   					portMedia[node_id].push_back(v.second.get<string>("<xmlattr>.component"));
					break;
					case MEDIA:
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "id=" + v.second.get<string>("<xmlattr>.id"));
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "src=" + v.second.get<string>("<xmlattr>.src"));
	   					id=v.second.get<string>("<xmlattr>.id");
	   					mediaSrc[id]=v.second.get<string>("<xmlattr>.src");
	   					mediaType[id]=getMediaTypeFromSrc(v.second.get<string>("<xmlattr>.src"));
	   					mediaDesc[id]=v.second.get<string>("<xmlattr>.descriptor");
					break;
					case LINK:
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "xconnector=" + v.second.get<string>("<xmlattr>.xconnector"));
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "id=" + v.second.get<string>("<xmlattr>.id"));
	   					id=v.second.get("<xmlattr>.id","0");
	   					linkId.push_back(id);
	   					connector[id]=(v.second.get<string>("<xmlattr>.xconnector"));
						loadData(id, v.second.get_child(""));
					break;
					case BIND:
	   					id=node_id;
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "id=" + id);
	   					bind[id]["role"]=v.second.get<string>("<xmlattr>.role");
	   					bind[id]["component"]=v.second.get<string>("<xmlattr>.component");
	   					//bind[id]["interface"]=v.second.get<string>("<xmlattr>.interface");
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "role=" + bind[id]["role"]);
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "component=" + bind[id]["component"]);
					break;
					case CAUSALCONNECTOR:
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "id=" + v.second.get<string>("<xmlattr>.id"));
	   					id=v.second.get<string>("<xmlattr>.id");
						loadData(id, v.second.get_child(""));
					break;
					case SIMPLECONDITION:
	   					id=node_id;
	   					condition[id]["role"]=v.second.get<string>("<xmlattr>.role");
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "role=" + bind[id]["role"]);
					break;
					case SIMPLEACTION:
	   					id=node_id;
	   					action[id]["role"]=v.second.get<string>("<xmlattr>.role");
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "role=" + bind[id]["role"]);
					break;
					case DESCRIPTOR:
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "id=" + v.second.get<string>("<xmlattr>.id"));
	   					id=v.second.get<string>("<xmlattr>.id");
	   					descRegion[id]=v.second.get<string>("<xmlattr>.region");
					break;
					case REGION:
						dbg.log(__PRETTY_FUNCTION__, "  " + node_name + "id=" + v.second.get<string>("<xmlattr>.id"));
	   					id=v.second.get<string>("<xmlattr>.id");
	   					region[id]["h"]=v.second.get<string>("<xmlattr>.height");
	   					region[id]["w"]=v.second.get<string>("<xmlattr>.width");
	   					region[id]["x"]=v.second.get<string>("<xmlattr>.left");
	   					region[id]["y"]=v.second.get<string>("<xmlattr>.top");
	   					region[id]["z"]=v.second.get<string>("<xmlattr>.zIndex");
					break;
					default:
					break;
					
		
				}
			}
    			catch (exception &e)
			{
				//cout << "Error: " << e.what() << "\n";
				dbg.log(__PRETTY_FUNCTION__,"Error: " + boost::lexical_cast<string>(e.what()));
			}
			//dbg.log(__PRETTY_FUNCTION__, "teste=" + v.second.data());
		}

	}

	MediaType getMediaTypeFromPort(string p)
	{ 
		MediaType m = mediaType[p];
		return m;
	}

	vector<string> getPorts(string id)
	{
		return portMedia[id];	
	}

	MediaType getMediaTypeFromSrc(string src)
	{ 
		vector<string> str;
		boost::split(str, src, boost::is_any_of("."));
		int i=(int)str.size()-1;

		dbg.log(__PRETTY_FUNCTION__, "extension=" + str[i] );
		return ext_type_map[str[i]];
	}

	string getRegionAttrib(string p, string id)
	{
		id = mediaDesc[id];
		id = descRegion[id];
		return region[id][p].empty()?"0":region[id][p];
	}

	//vector< map <string, string> > getMediasEvent(string e,string id)
	vector<string> getMediasTransition(string t,string id)
	{
		//return conectedMediasbyEvent[id, e];
		dbg.log(__PRETTY_FUNCTION__, t + ":" + id );
		switch(str_transition[t])
		{
			case STARTS:
				return onbegin[id];
			break;
			case STOPS:
				return onend[id];
			break;
			case PAUSES:
				return onpause[id];
			break;
			case RESUMES:
				return onresume[id];
			break;
			case ABORTS:
				return onabort[id];
			break;
			default:
			break;
		}
	}

	string getMediaSrc(string id)
	{
		return mediaSrc[id];
	}

	void linkConnectors()
	{
		string idc;
		BOOST_FOREACH( string id, linkId )
		{
			idc=connector[id];
			dbg.log(__PRETTY_FUNCTION__, id );
			dbg.log(__PRETTY_FUNCTION__, bind[id]["role"] );
			dbg.log(__PRETTY_FUNCTION__, bind[id]["component"] );
			dbg.log(__PRETTY_FUNCTION__, idc );
			dbg.log(__PRETTY_FUNCTION__, condition[idc]["role"] );
			dbg.log(__PRETTY_FUNCTION__, action[idc]["role"] );
		}
	}

	string getVersion()
	{
		return "Version 0.1 - 2012";
	}
};

